package com.nx.platform.es.bean.modle.query;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Range;
import com.google.common.primitives.Ints;
import com.nx.platform.es.biz.wrapper.parser.StatementOperator;
import com.nx.platform.es.bean.dto.RequestContext;
import com.nx.platform.es.common.utils.DateFormatUtil;
import org.apache.commons.collections4.MapUtils;
import org.elasticsearch.common.Strings;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;

import java.text.ParseException;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author
 * @since 2016年10月15日
 */
public class DateHandler implements QueryFieldHandler {

    private static final Pattern FORMAT_SINGLE = Pattern.compile("^\\d{1,2}$");
    private static final Pattern PATTERN_DATE_RANGE = Pattern.compile("^(\\*|\\d{8,10})_(\\*|\\d{8,10})$");

    @Override
    public void handle(ImmutableMap<String, ?> fieldConfig, RequestContext context, String fieldFace,
                       StatementOperator operator, String fieldValue, ListMultimap<Boolean, QueryBuilder> queryBuilders) {
        // 空值
        if (Strings.isNullOrEmpty(fieldValue)) {
            return;
        }
        // 记录Query信息(Trace)
        context.getTrace().append("&").append(fieldFace).append(operator.getSymbol()).append(fieldValue);
        //
        String face = MapUtils.getString(fieldConfig, "face");
        String fieldName = MapUtils.getString(fieldConfig, "fieldName");
        Preconditions.checkState(!Strings.isNullOrEmpty(fieldName));
        if (FORMAT_SINGLE.matcher(fieldValue).matches()) {
            Integer count = Ints.tryParse(fieldValue);
            if (count == null || count <= 0) {
                throw new IllegalArgumentException("format incorrect: field=" + face + ", value=" + fieldValue);
            }
            long to = LocalDate.now().toDate().getTime() + 3600000L * (LocalTime.now().getHourOfDay() + 1);
            long from = to - 3600000L * (count * 24 + 1);
            queryBuilders.put(operator.isSign(),
                    QueryBuilders.rangeQuery(fieldName).from(from).to(to).includeLower(true).includeUpper(true));
            return;
        }
        // 区间
        Matcher rangeMatcher = PATTERN_DATE_RANGE.matcher(fieldValue);
        if (rangeMatcher.find()) {
            try {
                String left = rangeMatcher.group(1);
                String right = rangeMatcher.group(2);
                if (left.equals(right) && !"*".equals(left)) {
                    queryBuilders.put(operator.isSign(),
                            QueryBuilders.termQuery(fieldName, parse(left).getTime()));
                    return;
                } else if (!"*".equals(left) && !"*".equals(right)) {
                    Date ln = parse(left);
                    Date rn = parse(right);
                    try {
                        Range<Date> range = Range.openClosed(ln, rn);
                        queryBuilders.put(operator.isSign(),
                                QueryBuilders.rangeQuery(fieldName).from(range.lowerEndpoint().getTime())
                                        .to(range.upperEndpoint().getTime()).includeLower(true).includeUpper(true));
                        return;
                    } catch (Exception nfe) {
                        // Empty OK
                    }
                } else if (!"*".equals(left)) {
                    Date ln = parse(left);
                    queryBuilders.put(operator.isSign(),
                            QueryBuilders.rangeQuery(fieldName).gte(ln.getTime()));
                    return;
                } else if (!"*".equals(right)) {
                    Date rn = parse(right);
                    queryBuilders.put(operator.isSign(),
                            QueryBuilders.rangeQuery(fieldName).lte(rn.getTime()));
                    return;
                }
            } catch (ParseException e) {
                throw new IllegalArgumentException("format incorrect: field=" + face + ", value=" + fieldValue, e);
            }
        }
        throw new IllegalArgumentException("format incorrect: field=" + face + ", value=" + fieldValue);
    }

    private Date parse(String str) throws ParseException {
        if (str.length() == 8) {
            return DateFormatUtil.parse("yyyyMMdd", str);
        } else {
            return DateFormatUtil.parse("yyyyMMddHH", str);
        }
    }

}
